﻿using System;
using System.Collections.Generic;

namespace DailyFx.UI
{

	/// <summary>
	///		Indicates stuff can be closed .
	/// </summary>
	public interface IUIGuardClose
	{
		/// <summary>
		///		Try to close this ... thing ?
		/// </summary>
		void TryClose();


		/// <summary>
		///		Checks if this can be closed .
		/// </summary>
		void CanClose ( Action<bool> callback );
	
	}

	/// <summary>
	///		Used to gather the results from multiple child elements which may or may not prevent closing.
	/// </summary>
	public interface IClosePolicy<T>
	{
		/// <summary>
		///		Applies the strategy.
		/// </summary>
		void Apply ( IEnumerable<T> toClosed, Action<bool, IEnumerable<T>> callback );
	}

	/// <summary>
	/// 
	/// </summary>
	public class DefaultClosePolicy<T> : IClosePolicy<T>
	{
		/// <summary>
		///		the items to be applied to close .
		/// </summary>
		List<T> _closables;

		/// <summary>
		///		the result after applyied closing
		/// </summary>
		bool _finalResult;

		/// <summary>
		///		forces guard evaluation .
		/// </summary>
		bool _forceGuardEval;

		/// <summary>
		///		forces to close the items regardless the conductor . 
		/// </summary>
		readonly bool _forceToClose;

		
        /// <summary>
        ///		Creates an instance of the class.
        /// </summary>
		public DefaultClosePolicy ( bool forceToClose = false )
		{
			this._forceToClose = forceToClose ;
        }

        /// <summary>
        ///		Applies the strategy.
        /// </summary>
        public void Apply ( IEnumerable<T> toClosed, Action<bool, IEnumerable<T>> callback )
		{
			this._closables = new List<T>();
            this._finalResult = true;
            this._forceGuardEval = false;

            this.Apply ( true, toClosed.GetEnumerator(), callback );
        }

        void Apply ( bool result, IEnumerator<T> toClosed, Action<bool, IEnumerable<T>> callback)
		{
            this._finalResult = this._finalResult && result;

            var guardPending = false;
            do {
                if (!toClosed.MoveNext())
				{
                    callback( this._finalResult, this._forceToClose ? this._closables : new List<T>() );
                    this._closables = null;
                    break;
                }

                var c = toClosed.Current;
                var guard = c as IUIGuardClose ;
                if (guard != null) 
				{
                    guardPending = true;
                    guard.CanClose ( canClose =>
					{
                        guardPending = false;
                        if (canClose) 
						{
                            this._closables.Add ( c);
                        }
                        if ( this._forceGuardEval ) 
						{
                            this._forceGuardEval = false;
                            this.Apply ( canClose, toClosed, callback );
                        } 
						else 
						{
                            _finalResult = _finalResult && canClose;  
                        }
                    });

                    this._forceGuardEval = this._forceGuardEval || guardPending;
                } 
				else 
				{
                    this._closables.Add ( c);
                }

            } while ( !guardPending );
        }
	}

}
